#include "ubvm_partial_torus.h"
#include "DIVA_Manager.h"
#include "xParameterPack.h"
#include "vmLibrary.h"
#include "EntityMaterialInstance.h"

#include <sstream>

const int NSIDES = 30 ;
const int NRINGS = 100 ;
const float PI = 3.14159 ;
int ubvm_partial_torus::_UNIQUE_INT = 0 ;
ubvm_partial_torus::ubvm_partial_torus(xParameterPack xpp):DIVA_VM(xpp){

	int radius_port = vmLibrary::instance().get_vm_default_input_parameters().size();
	_vm_center_radius = xpp.paras[radius_port].val_int_ptr ; // set up the private attributes
	_vm_ring_radius = xpp.paras[radius_port+1].val_int_ptr ;
	_vm_start_theda = xpp.paras[radius_port+2].val_float_ptr ;
	_vm_end_theda = xpp.paras[radius_port+3].val_float_ptr ;
	_vm_start_degree = xpp.paras[radius_port+4].val_int_ptr ;
	_vm_end_degree = xpp.paras[radius_port+5].val_int_ptr ;

	if (_vm_start_theda != NULL) _start_theda = (*_vm_start_theda);
	else _start_theda = 0 ;

	if (_vm_end_theda != NULL) _end_theda = (*_vm_end_theda);
	else _end_theda = 2 * PI ;

	if (_vm_start_degree != NULL) _start_theda = ((float)(*_vm_start_degree)/360) * 2 * PI ;

	if (_vm_end_degree != NULL) _end_theda = ((float)(*_vm_end_degree)/360) *2 * PI ;

	assert(_start_theda + 1/40*PI < _end_theda); // there must have at least one ring are generated

	init_default_parameters();
}
/// destructor
ubvm_partial_torus::~ubvm_partial_torus(){
}

void ubvm_partial_torus::load_3Dresources() {

	const int COLOR_MODULATE_PARAM_INDEX = 1 ;
	_mgr = DIVA_Manager::getInstance()->getSceneManager();
	
	_manual_name = "partial_torus"+ Ogre::StringConverter::toString(_UNIQUE_INT) ;
	_manual = _mgr->createManualObject(_manual_name);

	_manual->begin("TextureModColor", Ogre::RenderOperation::OT_TRIANGLE_LIST);
	    float r = (float) (*_vm_ring_radius);
		float R = (float) (*_vm_center_radius);
		partial_doughnut(r,R,NSIDES, NRINGS, _manual, _start_theda, _end_theda); // 4 sides and  8 rings
    _manual->end();

	// convert it to mesh, because we want to change the color 
	_meshptr = _manual->convertToMesh("partial_donut"+_UNIQUE_INT);
	_mesh_name = "partial_donut"+_UNIQUE_INT ;
	
	_ent = _mgr->createEntity(getNewUniqueName(), _meshptr->getName());
	_partial_node = _SceneNode->createChildSceneNode();
	_partial_node->attachObject(_ent);
	//Ogre::SubEntity* sub = _ent->getSubEntity(0);
	//sub->setCustomParameter(COLOR_MODULATE_PARAM_INDEX,Vector4(1,0,0,1));

	_manual_fake = _mgr->createManualObject("fake_partial_torus"+_UNIQUE_INT);
	_manual_fake_name = "fake_partial_torus"+_UNIQUE_INT ;
	_manual_fake->begin("TextureModColor", Ogre::RenderOperation::OT_TRIANGLE_LIST);
	    r = (float) (*_vm_ring_radius);
	    R = (float) (*_vm_center_radius);
		partial_doughnut(r,R,NSIDES, NRINGS, _manual_fake, 0, 2*PI); // 4 sides and  8 rings
    _manual_fake->end();

	// convert it to mesh, because we want to change the color 
	_meshptr_fake = _manual_fake ->convertToMesh("fake_partial_donut"+_UNIQUE_INT);
	_mesh_fake_name = "fake_partial_donut"+_UNIQUE_INT ;
	_UNIQUE_INT++ ;
	try {
		_ent_fake = _mgr->createEntity(getNewUniqueName(), _meshptr_fake->getName());
	} catch( Ogre::ItemIdentityException & e ) {
		// TODO convert this exception
		assert( !"create entity failed" );
	}


	_background_node = _SceneNode->createChildSceneNode();
	_background_node->attachObject(_ent_fake);

	Ogre::SubEntity* sub2 = _ent_fake->getSubEntity(0);
	sub2->setCustomParameter(COLOR_MODULATE_PARAM_INDEX,Ogre::Vector4(0,1,1,0.2));
    //_mgr->getRootSceneNode()->createChildSceneNode()->attachObject(manual);
	//_ent = VL_createEntity(meshName,"TextureModColor");
	//_SceneNode->attachObject(_ent);
}
void ubvm_partial_torus::set_default_values() {

	_vm_rx_val = 0 ;
	_vm_ry_val = 0 ;
	_vm_rz_val = 0 ;

	Ogre::Vector3 sz = _ent->getBoundingBox().getSize();
	_vm_init_sizex = sz.x ;
	_vm_init_sizey = sz.y ;
	_vm_init_sizez = sz.z ;

	_vm_sizex_val = sz.x ;
	_vm_sizey_val = sz.y ;
	_vm_sizez_val = sz.z ;

	_vm_sizexscale_val = 1.0 ;
	_vm_sizeyscale_val = 1.0 ;
	_vm_sizezscale_val = 1.0 ;

	_vm_r_val = 1.0;
	_vm_g_val = 1.0;
	_vm_b_val = 1.0;
	_vm_transparency_val = 1 ; // no transparency 

	_vm_yaw_val = 0 ;
	_vm_pitch_val = 0 ;
	_vm_roll_val = 0 ;

}
void ubvm_partial_torus::reg_event_entity() {
	
	// register the relation of the entity and vm in diva manager
	// When this entity is clicked, mouse/keyboard VM will be passed to this VM
	// By default it can be only one entity representing this VM

	// NOTE! you need to register the right entity in order for dragging to be working
	DIVA_Manager::getInstance()->reg_MovableObj_VM_relation (_ent_fake,this); 
	DIVA_Manager::getInstance()->reg_MovableObj_VM_relation (_ent,this); 

}
void ubvm_partial_torus::init_private_attrs_started() {
	int radius_port = vmLibrary::instance().get_vm_default_input_parameters().size();
	
}

void ubvm_partial_torus::init_private_attrs_ended() {}

void ubvm_partial_torus::VMValueUpdated() {
	const int COLOR_MODULATE_PARAM_INDEX = 1 ;
	int radius_port = vmLibrary::instance().get_vm_default_input_parameters().size();
	if (_para_onoff[radius_port] || _para_onoff[radius_port+1] || 
		_para_onoff[radius_port+2] || _para_onoff[radius_port+3] ||
		_para_onoff[radius_port+4] || _para_onoff[radius_port+5] ) {
	
		// first, we should free the previous created resources
		_partial_node->detachObject(_ent);
		_mgr->destroyManualObject(_manual);
		_mgr->destroyEntity(_ent); 
		Ogre::MeshManager::getSingleton().remove(_mesh_name); 

		if (_vm_start_theda != NULL) _start_theda = (*_vm_start_theda);
		if (_vm_end_theda != NULL) _end_theda = (*_vm_end_theda);
		if (_vm_start_degree != NULL) _start_theda = ((float)(*_vm_start_degree)/360) * 2 * PI ;
		if (_vm_end_degree != NULL) _end_theda = ((float)(*_vm_end_degree)/360)* 2* PI ;

		assert(_start_theda + 1/40*PI < _end_theda); // there must have at least one ring are generated

		// now, we create another new donut according to the updated values
		_manual = _mgr->createManualObject("partial_torus"+_UNIQUE_INT);
		_manual->begin("TextureModColor", Ogre::RenderOperation::OT_TRIANGLE_LIST);
			partial_doughnut((*_vm_ring_radius),(*_vm_center_radius),NSIDES, NRINGS, _manual, _start_theda, _end_theda); // 4 sides and  8 rings
		_manual->end();
		_manual_name = "partial_torus"+_UNIQUE_INT ;
		// convert it to mesh, because we want to change the color 
		_meshptr = _manual->convertToMesh("partial_donut"+_UNIQUE_INT);
		_ent = _mgr->createEntity(getNewUniqueName(), _meshptr->getName());
		_partial_node->attachObject(_ent);
		_mesh_name = "partial_donut"+_UNIQUE_INT ;

		DIVA_Manager::getInstance()->dereg_MovableObj_VM_relation(_ent_fake,this);  // deregister the entity and vm relation
		// second, we should the free the background torus
		_background_node->detachObject(_ent_fake);
		_mgr->destroyManualObject(_manual_fake);
		_mgr->destroyEntity(_ent_fake); 
		Ogre::MeshManager::getSingleton().remove(_mesh_fake_name); 

		_manual_fake = _mgr->createManualObject("fake_partial_torus"+_UNIQUE_INT);
		_manual_fake->begin("TextureModColor", Ogre::RenderOperation::OT_TRIANGLE_LIST);
			partial_doughnut((*_vm_ring_radius),(*_vm_center_radius),NSIDES, NRINGS, _manual_fake, 0, 2*PI); // 4 sides and  8 rings
		_manual_fake->end();
		_manual_fake_name = "fake_partial_torus"+_UNIQUE_INT ;

		// convert it to mesh, because we want to change the color 
		_meshptr_fake = _manual_fake ->convertToMesh("fake_partial_donut"+_UNIQUE_INT);
		_ent_fake = _mgr->createEntity(getNewUniqueName(), _meshptr_fake->getName());
		_mesh_fake_name = "fake_partial_donut"+_UNIQUE_INT ;
		_UNIQUE_INT++ ;

		_background_node = _SceneNode->createChildSceneNode();
		_background_node->attachObject(_ent_fake);

		Ogre::SubEntity* sub2 = _ent_fake->getSubEntity(0);
		sub2->setCustomParameter(COLOR_MODULATE_PARAM_INDEX,Ogre::Vector4(0,1,1,0.2));
			
		DIVA_Manager::getInstance()->reg_MovableObj_VM_relation(_ent_fake,this); // register the new entity and vm relation
		                                                                         // if this step is not done, the dragging will
																				 // not working
		
	}
	
	// OK, so far, we have finish handling the additional parameters of this VM
	// now, we call the DIVA_VM::VMvalueupdate() to deal with the default parameters
	DIVA_VM::VMValueUpdated(); 
}

void ubvm_partial_torus::setBodyScale(Ogre::Vector3 scale) {
	_SceneNode ->setScale(scale);
}
bool ubvm_partial_torus::isBodyScaleWrtSceneNode() { return true ; }

Ogre::Vector3 ubvm_partial_torus::getOriginalBodySize() {
	Ogre::AxisAlignedBox temp = _ent_fake ->getBoundingBox();	// here, we use _ent_fake as the bounding box
	Ogre::Vector3 size = temp.getSize(); 
	return size ;
}

Ogre::Vector3 ubvm_partial_torus::getActualSize() {
	return getOriginalBodySize() * _SceneNode->getScale();
}
Ogre::Vector3 ubvm_partial_torus::getBodyScale() {
	return _SceneNode->getScale();
}

void ubvm_partial_torus::partial_doughnut(float r, float R, int nsides, int rings, Ogre::ManualObject *manual, 
										  float start_theda, float end_theda)
{
  int i, j;
  const float M_PI = 3.14159 ;
  float theta, phi, theta1, phi1;
  float p0[03], p1[3], p2[3], p3[3];
  float n0[3], n1[3], n2[3], n3[3];
  int index ;

  index = 0 ;
  for (i = 0; i < rings ; i++) { //rings

	theta =  (float) i *2.0 * M_PI / rings + 1.5* M_PI ;  
    theta1 = (float) (i + 1) * 2.0 * M_PI / rings + 1.5* M_PI;

	if (((float) i * 2.0 * M_PI / rings) < start_theda) continue ;
	if (((float) (i + 1) * 2.0 * M_PI / rings) > end_theda) break ;

    for (j = 0; j < nsides ; j++) { //sides
      phi = (float) j *2.0 * M_PI / nsides;
      phi1 = (float) (j + 1) * 2.0 * M_PI / nsides;

      p0[0] = cos(theta) * (R + r * cos(phi));
      p0[1] = -sin(theta) * (R + r * cos(phi));
      p0[2] = r * sin(phi);

      p1[0] = cos(theta1) * (R + r * cos(phi));
      p1[1] = -sin(theta1) * (R + r * cos(phi));
      p1[2] = r * sin(phi);

      p2[0] = cos(theta1) * (R + r * cos(phi1));
      p2[1] = -sin(theta1) * (R + r * cos(phi1));
      p2[2] = r * sin(phi1);

      p3[0] = cos(theta) * (R + r * cos(phi1));
      p3[1] = -sin(theta) * (R + r * cos(phi1));
      p3[2] = r * sin(phi1);

      n0[0] = cos(theta) * (cos(phi));
      n0[1] = -sin(theta) * (cos(phi));
      n0[2] = sin(phi);

      n1[0] = cos(theta1) * (cos(phi));
      n1[1] = -sin(theta1) * (cos(phi));
      n1[2] = sin(phi);

      n2[0] = cos(theta1) * (cos(phi1));
      n2[1] = -sin(theta1) * (cos(phi1));
      n2[2] = sin(phi1);

      n3[0] = cos(theta) * (cos(phi1));
      n3[1] = -sin(theta) * (cos(phi1));
      n3[2] = sin(phi1);

	
	  manual->position(Ogre::Vector3(p0[0],p0[1],p0[2])); 
	  manual->position(Ogre::Vector3(p2[0],p2[1],p2[2]));
	  manual->position(Ogre::Vector3(p1[0],p1[1],p1[2]));
	  manual->index(index);
	  manual->index(index+1);
	  manual->index(index+2);
	  index += 3 ;

	  manual->position(Ogre::Vector3(p0[0],p0[1],p0[2]));  
	  manual->position(Ogre::Vector3(p3[0],p3[1],p3[2])); 
	  manual->position(Ogre::Vector3(p2[0],p2[1],p2[2]));
	  manual->index(index);
	  manual->index(index+1);
	  manual->index(index+2);
	  index += 3 ;
 
    }
  }
}